package sh

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"soluty.cc/go/xlog"
)

func ignoreError(f func() error) {
	_ = f()
}

// Exists 判断某个文件或者目录是否存在
func Exists(file string) (bool, error) {
	fi, err := os.Stat(file)
	if err == nil && fi.IsDir() {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, xlog.Errorf(xlog.Fmt(), xlog.Args(file), err)
}

// DirExists 判断是否存在某个目录
func DirExists(dir string) (bool, error) {
	fi, err := os.Stat(dir)
	if err == nil && fi.IsDir() {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, xlog.Errorf(xlog.Fmt(), xlog.Args(dir), err)
}

// ListFileAndDir 非递归的列出所有的文件和目录，通过glob匹配
func ListFileAndDir(path string, glob ...string) ([]os.FileInfo, []os.FileInfo, error) {
	b, err := DirExists(path)
	if !b {
		return nil, nil, xlog.Errorf(xlog.Fmt("%v"), xlog.Args(path, glob), "检查路径是否是目录", err)
	}
	dir, err := os.Open(path)
	if err != nil {
		return nil, nil, xlog.Errorf(xlog.Fmt("%v"), xlog.Args(path, glob), "打开所在路径", err)
	}
	defer ignoreError(dir.Close)
	infos, err := dir.Readdir(-1)
	if err != nil {
		return nil, nil, xlog.Errorf(xlog.Fmt("%v"), xlog.Args(path, glob), "读取目录", err)
	}
	var files []os.FileInfo
	var dirs []os.FileInfo
	for _, info := range infos {
		if len(glob) > 0 {
			match, err := filepath.Match(glob[0], info.Name())
			if err != nil {
				return nil, nil, xlog.Errorf(xlog.Fmt("%v:%v"), xlog.Args(path, glob), "匹配名字", info.Name(), err)
			}
			if !match {
				continue
			}
		}
		if info.IsDir() {
			dirs = append(dirs, info)
		} else {
			files = append(files, info)
		}
	}
	return files, dirs, nil
}

// ReadLines 按行读取某文本文件内容
func ReadLines(file string) (lines []string, err error) {
	fd, err := os.Open(file)
	if err != nil {
		return nil, xlog.Errorf(xlog.Fmt(), xlog.Args(file), err)
	}
	defer ignoreError(fd.Close)
	scanner := bufio.NewScanner(fd)
	scanner.Split(bufio.ScanLines)
	lines = make([]string, 0)
	for scanner.Scan() {
		l := scanner.Text()
		lines = append(lines, l)
	}
	return
}

func isDir(s string) bool {
	s = strings.TrimSpace(s)
	if s == "" {
		return false
	}
	if s[len(s)-1] == '/' || s[len(s)-1] == '\\' {
		return true
	}
	return false
}

// CopyFile 拷贝文件src到dst src必须是存在的文件, dst可以是文件也可以是目录, src必须是文件
// 如果dst是一个存在的目录，则拷贝到目录中，文件名和src一致， 如果dst是一个不存在的目录， 则尝试创建目录然后拷贝文件
func CopyFile(src, dst string) error {
	var dirString string
	var dstString string
	var dstIsDir = isDir(dst)
	if dstIsDir {
		dirString = dst
		dstString = filepath.Join(dst, filepath.Base(src))
	} else {
		dirString = filepath.Dir(dst)
		dstString = dst
	}
	_, err := os.Stat(dirString)
	if err != nil {
		if err = os.MkdirAll(dirString, os.ModePerm); err != nil {
			return xlog.Errorf(xlog.Fmt("创建目录%v"), xlog.Args(src, dst), dirString, err)
		}
	}
	dstFile, err := os.Create(dstString)
	if err != nil {
		return xlog.Errorf(xlog.Fmt(), xlog.Args(src, dst), err)
	}
	defer ignoreError(dstFile.Close)
	srcFile, err := os.Open(src)
	if err != nil {
		return fmt.Errorf(xlog.Fmt("打开原文件"), xlog.Args(src, dst), err)
	}
	defer ignoreError(srcFile.Close)
	srcInfo, err := srcFile.Stat()
	if err != nil {
		return fmt.Errorf(xlog.Fmt(), xlog.Args(src, dst), err)
	}
	if err = os.Chmod(dstFile.Name(), srcInfo.Mode()); err != nil {
		return xlog.Errorf(xlog.Fmt(), xlog.Args(src, dst), err)
	}
	if _, err = io.Copy(dstFile, srcFile); err != nil {
		return xlog.Errorf(xlog.Fmt(), xlog.Args(src, dst), err)
	}
	return nil
}

// CopyDir 目录深拷贝, src必须是存在的目录
func CopyDir(src, dst string, replace ...bool) error {
	existSrc, err := DirExists(src)
	if err != nil {
		return xlog.Errorf(xlog.Fmt(), xlog.Args(src, dst, replace), err)
	}
	if !existSrc {
		return xlog.Errorf("%v | 源目录不存在", xlog.Args(src, dst, replace))
	}
	srcInfo, _ := os.Stat(src)
	err = os.MkdirAll(dst, os.FileMode(0755))
	if err != nil {
		return xlog.Errorf(xlog.Fmt("创建目标路径"), xlog.Args(src, dst, replace), err)
	}
	defer func() {
		_ = os.Chmod(dst, srcInfo.Mode())
	}()
	files, dirs, err := ListFileAndDir(src)
	if err != nil {
		return xlog.Errorf(xlog.Fmt(), xlog.Args(src, dst, replace), err)
	}
	if len(replace) > 0 && replace[0] {
		err := os.RemoveAll(dst)
		if err != nil {
			return xlog.Errorf(xlog.Fmt("%v"), xlog.Args(src, dst, replace), "删除目标路径", err)
		}
	}
	for _, file := range files {
		fileName := filepath.Join(src, file.Name())
		fileDst := filepath.Join(dst, file.Name())
		err := CopyFile(fileName, fileDst)
		if err != nil {
			return xlog.Errorf(xlog.Fmt("拷贝文件%v"), xlog.Args(src, dst, replace), file.Name(), err)
		}
	}
	for _, dir := range dirs {
		dirName := filepath.Join(src, dir.Name())
		dirDst := filepath.Join(dst, dir.Name())
		err := CopyDir(dirName, dirDst)
		if err != nil {
			return xlog.Errorf(xlog.Fmt("拷贝目录%v"), xlog.Args(src, dst, replace), dir.Name(), err)
		}
	}
	return nil
}
